package com.keta.foundation.controller;

import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;

import javax.servlet.ServletRequest;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.validation.Valid;

import org.apache.commons.io.IOUtils;
import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.propertyeditors.CustomDateEditor;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.annotation.InitBinder;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.multipart.MultipartFile;

import com.keta.foundation.dataFilter.DynamicSpecifications;
import com.keta.foundation.entity.FileResource;
import com.keta.foundation.log.Log;
import com.keta.foundation.log.LogUtil;
import com.keta.foundation.service.FileResourceService;
import com.keta.foundation.util.Exceptions;
import com.keta.foundation.util.FileUtils;
import com.keta.foundation.util.Identities;
import com.keta.foundation.util.ServletUtils;
import com.keta.foundation.util.dwz.AjaxObject;
import com.keta.foundation.util.dwz.Page;





@Controller
@RequestMapping("/fileResource")
public class FileResourceController {

	private static final Logger logger = LoggerFactory.getLogger(FileResourceController.class);

	private static final String FILERESOURCE_LIST = "fileResource/list";
	private static final String FILERESOURCE_CREATE = "fileResource/upload";
	private static final String FILERESOURCE_EDIT = "fileResource/edit";
	
	private static final String STORE_DIR = "/upload";

	@Autowired
	private FileResourceService fileResourceService;
	
	@InitBinder
	public void dataBinder(WebDataBinder dataBinder) {
		DateFormat df = new SimpleDateFormat("yyyy-MM-dd");
		dataBinder.registerCustomEditor(Date.class, new CustomDateEditor(df, true));
	}

	/**
	 * 文件资源列表入口
	 */
	@RequiresPermissions("FileResource:view")
	@RequestMapping("/list")
	public String list(ServletRequest request, Page page, Model model) {
		Specification<FileResource> specification = DynamicSpecifications.byDataFilter(request, FileResource.class);
		List<FileResource> fileResourceList = fileResourceService.getAll(specification, page);
		model.addAttribute("fileResourceList", fileResourceList);
		model.addAttribute("page", page);

		return FILERESOURCE_LIST;
	}
	
	/**
	 * 添加文件资源
	 */
	@RequiresPermissions("FileResource:upload")
	@RequestMapping(value = "/upload/{storeType}", method = RequestMethod.GET)
	public String preUpload(@PathVariable String storeType,Model model) {
		model.addAttribute("storeType", storeType);
		return FILERESOURCE_CREATE;
	}
	
	
	/**
	 * 添加文件资源
	 */
	@ResponseBody
	@RequestMapping(value = "/upload", method = RequestMethod.POST)
	@Log(message = "上传了文件:{0}")
	@RequiresPermissions("FileResource:upload")
	public ResponseEntity<String> upload(HttpServletRequest request, 
			String storeType, @RequestParam MultipartFile[] files) {
		
		for (MultipartFile file : files) {
			switch (storeType) {
				case "file":
					String uploadPath = request.getSession().getServletContext().getRealPath("/") + STORE_DIR;
					return upload2File(file, uploadPath);
				case "db":
					return upload2Db(file);
				default:
					return new ResponseEntity<String>("上传文件错误。", HttpStatus.INTERNAL_SERVER_ERROR);
			}
		}
		
		
		//LogUtil.putArgs(new Object[] { fileResource.toString() });
		return new ResponseEntity<String>(HttpStatus.OK);
	}
	
	private ResponseEntity<String> upload2File(MultipartFile file, String uploadPath) {
		// 检查扩展名
		String fileName = file.getOriginalFilename();
		String fileExt = FileUtils.getFileExt(fileName);
		
		String uuid = Identities.uuid2();
		String newFileName = uuid + "." + fileExt;
		
		File uploadedFile = new File(uploadPath, newFileName);
		try {
			org.apache.commons.io.FileUtils.writeByteArrayToFile(uploadedFile, file.getBytes());
			
			FileResource resource = new FileResource();
			resource.setName(file.getOriginalFilename());
			resource.setSize(FileUtils.getHumanReadableFileSize(file.getSize()));
			resource.setUploadTime(new Date());
			resource.setFileType(fileExt);
			resource.setUuid(uuid);
			resource.setStoreType("file");
			
			fileResourceService.saveOrUpdate(resource);
			
			LogUtil.putArgs(new Object[] { resource.getName()});
		} catch (Exception e) {
			if (uploadedFile.exists()) {
				uploadedFile.delete();
			}
			logger.error(Exceptions.getStackTraceAsString(e));
			return new ResponseEntity<String>(e.getMessage(), HttpStatus.INTERNAL_SERVER_ERROR);
		}
		
		return new ResponseEntity<String>(HttpStatus.OK);
	}
	
	private ResponseEntity<String> upload2Db(MultipartFile file) {
		
		String fileName = file.getOriginalFilename();
		String fileExt = FileUtils.getFileExt(fileName);
		
		FileResource resource = new FileResource();
		resource.setName(file.getOriginalFilename());
		resource.setSize(FileUtils.getHumanReadableFileSize(file.getSize()));
		resource.setUploadTime(new Date());
		resource.setFileType(fileExt);
		resource.setUuid(Identities.uuid2());
		resource.setStoreType("db");
		
		try {
			resource.setFile(file.getBytes());
			fileResourceService.saveOrUpdate(resource);
			
			LogUtil.putArgs(new Object[] { resource.getName()});
		} catch (Exception e) {
			logger.error(Exceptions.getStackTraceAsString(e));
			return new ResponseEntity<String>(e.getMessage(), HttpStatus.INTERNAL_SERVER_ERROR);
		}
		
		return new ResponseEntity<String>(HttpStatus.OK);
	}
	
	@RequiresPermissions("FileResource:download")
	@RequestMapping(value = "/download/{uuid}", method = RequestMethod.GET)
	public @ResponseBody String download(HttpServletRequest request, HttpServletResponse response, 
			 @PathVariable String uuid) {
		FileResource resource = fileResourceService.getByUuid(uuid);
		if (resource == null) {
			return "找不到" + uuid + "文件或已被删除。";
		}
	
		switch (resource.getStoreType()) {
			case "file":
				String downloadPath = request.getSession().getServletContext().getRealPath("/") +
				STORE_DIR +File.separator +resource.getUuid()+"."+resource.getFileType();
				return downloadFromFile(request, response, resource, downloadPath);
			case "db":
				return downloadFromDb(request, response, resource);
			default:
				return "下载文件错误。";
		}
		
	}
	
	private String downloadFromFile(HttpServletRequest request, HttpServletResponse response, FileResource resource, String downloadPath) {

		InputStream input = null;
		downloadPath = downloadPath.replace("\\", File.separator);
		try {
			input = new FileInputStream(downloadPath);
		} catch (FileNotFoundException e) {
			logger.error(resource.getName() + "文件没找到或已被删除：" + Exceptions.getStackTraceAsString(e));
			return resource.getName() + "文件没找到或已被删除。";
		}
		
		ServletUtils.setFileDownloadHeader(request, response, resource.getName());
		
		try {
			IOUtils.copy(input, response.getOutputStream());
		} catch (Exception e) {
			logger.error(resource.getName() + "文件下载出错：" + Exceptions.getStackTraceAsString(e));
			return resource.getName() + "文件下载出错。";
		} finally {
			IOUtils.closeQuietly(input);
		}
		
		return null;
	}
	
	private String downloadFromDb(HttpServletRequest request, HttpServletResponse response, FileResource resource) {
		InputStream input = new ByteArrayInputStream(resource.getFile());
		ServletUtils.setFileDownloadHeader(request, response, resource.getName());
		
		try {
			IOUtils.copy(input, response.getOutputStream());
		} catch (Exception e) {
			logger.error(resource.getName() + "文件下载出错：" + Exceptions.getStackTraceAsString(e));
			return resource.getName() + "文件下载出错。";
		} finally {
			IOUtils.closeQuietly(input);
		}
		
		return null;
	}
	
	/**
	 * 更新文件资源
	 */
	@RequiresPermissions("FileResource:edit")
	@RequestMapping(value = "/edit/{id}", method = RequestMethod.GET)
	public String preEdit(@PathVariable Long id, Model model) {
		FileResource fileResource = fileResourceService.get(id);
		model.addAttribute("fileResource", fileResource);
		return FILERESOURCE_EDIT;
	}

	/**
	 * 更新文件资源
	 */
	@ResponseBody
	@RequestMapping(value = "/edit", method = RequestMethod.POST)
	@Log(message = "修改了用户:{0}")
	@RequiresPermissions("FileResource:edit")
	public String edit(@Valid FileResource fileResource) {

		fileResourceService.saveOrUpdate(fileResource);
		LogUtil.putArgs(new Object[] { fileResource.toString() });
		return AjaxObject.newOk("文件资源修改成功!").toString();
	}
	

	/*
	 * 删除文件资源入口
	 */
	@Log(message = "删除了主键为{0}的文件资源")
	@RequiresPermissions("FileResource:delete")
	@RequestMapping("/delete")
	public @ResponseBody String delete(Long[] ids,HttpServletRequest request) {
		String fileStr = "";
		for (Long id : ids) {
			FileResource resource = fileResourceService.get(id);
			if("file".equals(resource.getStoreType())){
				String filePath = request.getSession().getServletContext().getRealPath("/") +
						STORE_DIR +File.separator +resource.getUuid()+"."+resource.getFileType();
				File file = new File(filePath);
				if (file.exists()) {
					try {
						org.apache.commons.io.FileUtils.forceDelete(file);
					} catch (IOException e) {
						return AjaxObject.newError(resource.getName() + "文件删除失败，请稍后再试。")
								.setCallbackType("").toString();
					}
				}
			}
			fileResourceService.delete(id);
		}
		LogUtil.putArgs(new Object[] { fileStr });
		return AjaxObject.newOk("成功删除了" + ids.length + "条文件资源！").setCallbackType("").toString();
	}

}
